package me.wangxuan.pat.repository;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Id;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import me.wangxuan.pat.JpaConfig;

public class RepositoryProxy {
	private static final Logger logger = Logger.getLogger("global");
	private static EntityManagerFactory emf = JpaConfig.getEntityManagerFactory();

	@SuppressWarnings("unchecked")
	public static <T> T createProxy(Class<T> target) {
//        return (T) Proxy.newProxyInstance(
//            interfaceClass.getClassLoader(),
//            new Class<?>[]{interfaceClass},
//            handler
//        );

		return (T) Proxy.newProxyInstance(target.getClassLoader(), new Class<?>[] { target },
				new RepositoryHandler<T>(target, emf));
	}

	private static class RepositoryHandler<T> implements InvocationHandler {
		private final Class<T> target;
		private final EntityManagerFactory emf;

		public RepositoryHandler(Class<T> target, EntityManagerFactory emf) {
			this.target = target;
			this.emf = emf;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			String methodName = method.getName();
			logger.info("Before method: " + methodName + " args:" + args);
			EntityManager em = null;
			try {
				em = emf.createEntityManager();
				ParameterizedType parameterizedType = (ParameterizedType) target.getGenericInterfaces()[0];
				@SuppressWarnings("unchecked")
				Class<T> entityType = (Class<T>) parameterizedType.getActualTypeArguments()[0];

				if ("save".equals(methodName)) {
					Object entity = args[0];
					Object id = getPrimaryKeyValue(entity);
					em.getTransaction().begin();
					if(id == null) {
						em.persist(entity);
					}else {
						em.merge(entity);
					}
					em.getTransaction().commit();
				} else if ("findById".equals(methodName)) {
					Object id = args[0];
					return em.find(entityType, id);
				} else if ("findAll".equals(methodName)) {
					CriteriaBuilder cb = em.getCriteriaBuilder();
					CriteriaQuery<T> cq = cb.createQuery(entityType);
					Root<T> root = cq.from(entityType);
					cq.select(root);
					TypedQuery<?> query = em.createQuery(cq);
					return query.getResultList();
				} else if ("deleteById".equals(methodName)) {
					em.getTransaction().begin();
					Object toDelete = em.find(entityType, args[0]);
					if (toDelete != null) {
						em.remove(toDelete);
					}
					em.getTransaction().commit();
				} else if ("deleteAll".equals(methodName)) {
					em.getTransaction().begin();
					em.createQuery("DELETE FROM " + entityType.getSimpleName()).executeUpdate();
					em.getTransaction().commit();
				} else if("findByNativeSql".equals(methodName)) {
					String sql = (String)args[0];
					Object[] params = (Object[])args[1];
					Query query = em.createNativeQuery(sql,entityType);
					if(params != null) {
						for(int i=0;i<params.length;i++) {
							query.setParameter(i+1, params[i]);
						}
					}
			        return query.getResultList();
				}

				return null;
			} catch (Exception e) {
				if (em != null && em.getTransaction().isActive()) {
					em.getTransaction().rollback();
				}
				e.printStackTrace();
				throw e;
			} finally {
				if (em != null) {
					em.close();
				}
			}
		}
		
		Object getPrimaryKeyValue(Object entity) throws Exception {
	        Class<?> entityClass = entity.getClass();
	        
	        // 尝试从字段上获取 @Id 注解
	        for (Field field : entityClass.getDeclaredFields()) {
	            if (field.isAnnotationPresent(Id.class)) {
	                field.setAccessible(true);
	                return field.get(entity);
	            }
	        }
	        // 如果没有找到带 @Id 注解的字段，尝试从 getter 方法中获取
	        for (Method method : entityClass.getMethods()) {
	            if (method.isAnnotationPresent(Id.class)) {
	                return method.invoke(entity);
	            }
	        }
	        //throw new Exception("Entity does not have an @Id annotated field or getter method.");
	        return null;
	    }

	}
}
